/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#include <linux/soundcard.h>
#include <sound/asound.h>

#include "tinyalsa/asoundlib.h"

#include "audio_interface_lib_render.h"
#include "hdf_log.h"

#define HDF_LOG_TAG HDI_AUDIO_ALSA

struct AlsaCtx {
    struct pcm *pcm_handle;
    struct pcm_config config;

    int initFlag;
    int renderRefCount;
    float volume;  // volume in db [ (volMax - volMin) / 2 * log10(Vol) + volMin]
    int volMin;
    int volMax;
};

static struct AlsaCtx s_alsaCtx = {
    .initFlag = 0
};

struct AlsaDevObject {
    char serviceName[64];
    struct AlsaCtx *pAlsaCtx;
};


#define CHECK_IF_ALSA_OPENED(aCtx) do { \
    if (!((struct AlsaCtx*)aCtx)->pcm_handle) { \
        HDF_LOGE("Error: in %{public}s, Pcm is not opened!", __func__); \
        return HDF_FAILURE; \
    } \
} while(0)


static inline enum pcm_format ConverFormatToAlsa(enum AudioFormat format)
{
    switch (format) {
        case AUDIO_FORMAT_PCM_8_BIT:
            return PCM_FORMAT_S8;
        case AUDIO_FORMAT_PCM_16_BIT:
            return PCM_FORMAT_S16_LE;
        case AUDIO_FORMAT_PCM_24_BIT:
            return PCM_FORMAT_S24_LE;
        case AUDIO_FORMAT_PCM_32_BIT:
            return PCM_FORMAT_S32_LE;
        case AUDIO_FORMAT_AAC_MAIN:
        case AUDIO_FORMAT_AAC_LC:
        case AUDIO_FORMAT_AAC_LD:
        case AUDIO_FORMAT_AAC_ELD:
        case AUDIO_FORMAT_AAC_HE_V1:
        case AUDIO_FORMAT_AAC_HE_V2:
        case AUDIO_FORMAT_G711A:
        case AUDIO_FORMAT_G711U:
        case AUDIO_FORMAT_G726:
        default:
            return PCM_FORMAT_INVALID;
    }
}


static struct AlsaCtx* AlsaCtxInstanceGet()
{
    if (!s_alsaCtx.initFlag) {
        memset_s(&s_alsaCtx, sizeof(s_alsaCtx), 0, sizeof(s_alsaCtx));
        s_alsaCtx.renderRefCount = 0;
        s_alsaCtx.pcm_handle = NULL;
        s_alsaCtx.volMax = 100;
        s_alsaCtx.volMin = 0;
        s_alsaCtx.volume = 50.0;
        s_alsaCtx.initFlag = 1;
    }

    return &s_alsaCtx;
}


static int AlsaOpen(struct AlsaCtx *aCtx)
{
    int sound_card_id = 0;
    int sound_dev_id = 1;

    HDF_LOGI("%{public}s() rate=%{public}d, channels=%{public}d, format=%{public}d", __func__,
            aCtx->config.rate, aCtx->config.channels, aCtx->config.format);

    if (aCtx->pcm_handle) {
        HDF_LOGW("Alsa is opened already.");
        return 0;
    }
    
    //system("echo 0 > /sys/class/audiodsp/digital_codec");//set output codec type as pcm
    struct pcm_config config;
    memcpy(&config, &aCtx->config, sizeof(config));
    struct pcm *pcm = pcm_open(sound_card_id, sound_dev_id, PCM_OUT, &config);
    if (!pcm_is_ready(pcm)) {
        HDF_LOGE("Error: %{public}s() Cannot open pcm_out(card %{public}d, device %{public}d): %{public}s", __func__, 
                sound_card_id, sound_dev_id, pcm_get_error(pcm));
        pcm_close (pcm);
        
        return -1;
    }

    aCtx->pcm_handle = pcm;
    
    //usleep(10000);

    HDF_LOGI("Open Alsa Success.");
    
    return 0;
}


static int AlsaClose(struct AlsaCtx *aCtx)
{
    if (aCtx->pcm_handle) {
        pcm_close(aCtx->pcm_handle);
        aCtx->pcm_handle = NULL;
    }
    
    HDF_LOGI("Close Alsa Success.");
    
    return 0;
}


static int AlsaConfig(struct AlsaCtx *aCtx, const struct AudioHwRenderParam *handleData)
{
    struct pcm_config config;
    enum pcm_format format;
    
    if (handleData == NULL) {
        HDF_LOGE("Error: handleData is NULL!");
        return -1;
    }

    format = ConverFormatToAlsa(handleData->frameRenderMode.attrs.format);
    if (PCM_FORMAT_INVALID == format) {
        HDF_LOGE("Error: Unsupported format: %{public}d", handleData->frameRenderMode.attrs.format);
        return -1;
    }
    
    memset_s(&config, sizeof(config), 0, sizeof(config));

    config.channels = handleData->frameRenderMode.attrs.channelCount;
    config.rate = handleData->frameRenderMode.attrs.sampleRate;
    config.format = format;
    config.period_count = handleData->frameRenderMode.periodCount;   // 6
    config.period_size = handleData->frameRenderMode.periodSize;      // 512
    config.start_threshold = handleData->frameRenderMode.attrs.startThreshold;
    config.stop_threshold = handleData->frameRenderMode.attrs.stopThreshold;
    config.silence_threshold = handleData->frameRenderMode.attrs.silenceThreshold;
    
    HDF_LOGV("DUMP Alsa Config 1#: channels=%{public}d, rate=%{public}d, format=%{public}d, period_count=%{public}d, period_size=%{public}d", 
            config.channels, config.rate, config.format, config.period_count, config.period_size);
    HDF_LOGV("DUMP Alsa Config 2#: start_threshold=%{public}d, stop_threshold=%{public}d, silence_threshold=%{public}d", 
            config.start_threshold, config.stop_threshold, config.silence_threshold);

    HDF_LOGV("DUMP Alsa OldConfig 1#: channels=%{public}d, rate=%{public}d, format=%{public}d, period_count=%{public}d, period_size=%{public}d", 
            aCtx->config.channels, aCtx->config.rate, aCtx->config.format, aCtx->config.period_count, aCtx->config.period_size);
    HDF_LOGV("DUMP Alsa OldConfig 2#: start_threshold=%{public}d, stop_threshold=%{public}d, silence_threshold=%{public}d", 
            aCtx->config.start_threshold, aCtx->config.stop_threshold, aCtx->config.silence_threshold);

    if (!memcmp(&config, &aCtx->config, sizeof(config))) {
        HDF_LOGW("Warn: Same Audio Hw config. No need to change.");
        return 0;
    }

    memcpy(&aCtx->config, &config, sizeof(config));
    
    AlsaClose(aCtx);
    if (AlsaOpen(aCtx) < 0) {
        HDF_LOGE("Error: in %{public}s, AlsaOpen() failed.", __func__);
        return HDF_FAILURE;
    }

    HDF_LOGV("Config Alsa SUCCESS.");
    
    return 0;
}


static int32_t DoCtlRenderSetVolume(const struct DevHandle *handle, int cmdId, const struct AudioHwRenderParam *handleData)
{
    struct AlsaCtx *aCtx = ((struct AlsaDevObject*)handle->object)->pAlsaCtx;

    HDF_LOGV("INFO: enter %{public}s(). volume=%{public}f", __func__, 
            handleData->renderMode.ctlParam.volume);

    if (handleData->renderMode.ctlParam.volume < aCtx->volMin 
            || handleData->renderMode.ctlParam.volume > aCtx->volMax) {
        HDF_LOGE("Error: Invalid volume");
        return HDF_FAILURE;
    }
    
    aCtx->volume = handleData->renderMode.ctlParam.volume;
    
    return HDF_SUCCESS;
}


static int32_t DoCtlRenderGetVolume(const struct DevHandle *handle, int cmdId, struct AudioHwRenderParam *handleData)
{
    struct AlsaCtx *aCtx = ((struct AlsaDevObject*)handle->object)->pAlsaCtx;

    handleData->renderMode.ctlParam.volume = aCtx->volume;
    
    return HDF_SUCCESS;
}


static int32_t DoCtlRenderSetPauseStu(const struct DevHandle *handle,
    int cmdId, const struct AudioHwRenderParam *handleData)
{
    struct AlsaCtx *aCtx = ((struct AlsaDevObject*)handle->object)->pAlsaCtx;

    CHECK_IF_ALSA_OPENED(aCtx);

    if (pcm_ioctl(aCtx->pcm_handle, SNDRV_PCM_IOCTL_PAUSE, 
            handleData->renderMode.ctlParam.pause?1:0) < 0) {
        HDF_LOGE("Error: pcm_ioctl(SNDRV_PCM_IOCTL_PAUSE) failed: %{public}s\n", pcm_get_error(aCtx->pcm_handle));
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}


static int32_t DoCtlRenderSetMuteStu(const struct DevHandle *handle, int cmdId, const struct AudioHwRenderParam *handleData)
{
    //handleData->renderMode.ctlParam.mute;
    return HDF_SUCCESS;
}


static int32_t DoCtlRenderGetMuteStu(const struct DevHandle *handle, int cmdId, struct AudioHwRenderParam *handleData)
{
    handleData->renderMode.ctlParam.mute = false;
    return HDF_SUCCESS;
}


static int32_t DoCtlRenderSetGainStu(const struct DevHandle *handle,
    int cmdId, const struct AudioHwRenderParam *handleData)
{
    return HDF_SUCCESS;
}


static int32_t DoCtlRenderGetGainStu(const struct DevHandle *handle, int cmdId, struct AudioHwRenderParam *handleData)
{
    //handleData->renderMode.ctlParam.audioGain.gain = 0;
    return HDF_SUCCESS;
}


static int32_t DoCtlRenderSceneSelect(const struct DevHandle *handle,
    int cmdId, const struct AudioHwRenderParam *handleData)
{
    HDF_LOGV("INFO: enter %{public}s(). portId=%{public}d", __func__, 
            handleData->renderMode.hwInfo.deviceDescript.portId);

    HDF_LOGV("DUMP AudioHwRenderParam: %{public}d, %{public}d, %{public}d, %{public}d, %{public}d, %{public}d, %{public}d, %{public}d", 
            handleData->frameRenderMode.attrs.channelCount,
            handleData->frameRenderMode.attrs.sampleRate,
            handleData->frameRenderMode.attrs.format,
            handleData->frameRenderMode.periodCount,
            handleData->frameRenderMode.periodSize,
            handleData->frameRenderMode.attrs.startThreshold,
            handleData->frameRenderMode.attrs.stopThreshold,
            handleData->frameRenderMode.attrs.silenceThreshold);
    
    return HDF_SUCCESS;
}


static int32_t DoCtlRenderSceneGetGainThreshold(const struct DevHandle *handle,
    int cmdId, struct AudioHwRenderParam *handleData)
{
    handleData->renderMode.ctlParam.audioGain.gain = 0;
    handleData->renderMode.ctlParam.audioGain.gainMax = 50.0;
    handleData->renderMode.ctlParam.audioGain.gainMin = 0;
    return HDF_SUCCESS;
}


static int32_t DoCtlRenderGetVolThreshold(const struct DevHandle *handle, int cmdId, struct AudioHwRenderParam *handleData)
{
    struct AlsaCtx *aCtx = ((struct AlsaDevObject*)handle->object)->pAlsaCtx;

    handleData->renderMode.ctlParam.volThreshold.volMax = aCtx->volMax;
    handleData->renderMode.ctlParam.volThreshold.volMin = aCtx->volMin;
    
    return HDF_SUCCESS;
}


static int32_t DoCtlRenderSetChannelMode(const struct DevHandle *handle,
    int cmdId, const struct AudioHwRenderParam *handleData)
{
    return HDF_SUCCESS;
}


static int32_t DoCtlRenderGetChannelMode(const struct DevHandle *handle,
    int cmdId, struct AudioHwRenderParam *handleData)
{
    handleData->frameRenderMode.mode = AUDIO_CHANNEL_NORMAL;
    return HDF_SUCCESS;
}


static int32_t DoCtlRenderSetAcodecMode(const struct DevHandle *handle,
    const int cmdId, const struct AudioHwRenderParam *handleData)
{
    return HDF_SUCCESS;
}

static int32_t HandleCtlRenderCmd(struct DevHandle *handle, const int cmdId, struct AudioHwRenderParam *handleData)
{
    switch (cmdId) {
        case AUDIODRV_CTL_IOCTL_ELEM_READ:
            return DoCtlRenderGetVolume(handle, cmdId, handleData);
        case AUDIODRV_CTL_IOCTL_ELEM_WRITE:
            return DoCtlRenderSetVolume(handle, cmdId, handleData);
        case AUDIODRV_CTL_IOCTL_MUTE_READ:
            return DoCtlRenderGetMuteStu(handle, cmdId, handleData);
        case AUDIODRV_CTL_IOCTL_MUTE_WRITE:
            return DoCtlRenderSetMuteStu(handle, cmdId, handleData);
        case AUDIODRV_CTL_IOCTL_CHANNEL_MODE_READ:
            return DoCtlRenderGetChannelMode(handle, cmdId, handleData);
        case AUDIODRV_CTL_IOCTL_CHANNEL_MODE_WRITE:
            return DoCtlRenderSetChannelMode(handle, cmdId, handleData);
        case AUDIODRV_CTL_IOCTL_GAIN_WRITE:
            return DoCtlRenderSetGainStu(handle, cmdId, handleData);
        case AUDIODRV_CTL_IOCTL_GAIN_READ:
            return DoCtlRenderGetGainStu(handle, cmdId, handleData);
        case AUDIODRV_CTL_IOCTL_SCENESELECT_WRITE:
            return DoCtlRenderSceneSelect(handle, cmdId, handleData);
        case AUDIODRV_CTL_IOCTL_GAINTHRESHOLD_READ:
            return DoCtlRenderSceneGetGainThreshold(handle, cmdId, handleData);
        case AUDIODRV_CTL_IOCTL_ACODEC_CHANGE_IN:
        case AUDIODRV_CTL_IOCTL_ACODEC_CHANGE_OUT:
            return DoCtlRenderSetAcodecMode(handle, cmdId, handleData);
        case AUDIODRV_CTL_IOCTL_VOL_THRESHOLD_READ:
            return DoCtlRenderGetVolThreshold(handle, cmdId, handleData);
        default:
            HDF_LOGE("Error: Output Mode not support!");
            break;
    }
    return HDF_FAILURE;
}


static int32_t DoOutputRenderHwParams(const struct DevHandle *handle,
    const int cmdId, const struct AudioHwRenderParam *handleData)
{
    struct AlsaCtx *aCtx = ((struct AlsaDevObject*)handle->object)->pAlsaCtx;

    HDF_LOGV("INFO: enter %{public}s()", __func__);
    
    if (AlsaConfig(aCtx, handleData) < 0) {
        HDF_LOGE("Error: in %{public}s, AlsaConfig() failed.", __func__);
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}


static void PreProcessPcmData(struct AlsaCtx *aCtx, char *pcm, int size)
{
    float volGain = aCtx->volume / (float)(aCtx->volMax - aCtx->volMin);
    if ((int)volGain == 1) {
        return;
    }

    // FIXME
    if (PCM_FORMAT_S16_LE == aCtx->config.format) {
        short *ptr = (short*)pcm;
        for (int i = 0; i < size/2; i++) {
            *ptr++ *= volGain;
        }
    } else if (PCM_FORMAT_S8 == aCtx->config.format) {
        char *ptr = pcm;
        for (int i = 0; i < size; i++) {
            *ptr++ *= volGain;
        }
    } else if (PCM_FORMAT_S24_LE == aCtx->config.format
                || PCM_FORMAT_S32_LE == aCtx->config.format) {
        int *ptr = (int*)pcm;
        for (int i = 0; i < size/4; i++) {
            *ptr++ *= volGain;
        }
    }
}


static int32_t DoOutputRenderWrite(const struct DevHandle *handle,
    const int cmdId, const struct AudioHwRenderParam *handleData)
{
    struct AlsaCtx *aCtx = ((struct AlsaDevObject*)handle->object)->pAlsaCtx;

    //HDF_LOGV("INFO: enter %{public}s(). %{public}p, %{public}lld", __func__, handleData->frameRenderMode.buffer,
            //handleData->frameRenderMode.bufferSize);

    if (!handleData->frameRenderMode.buffer || 0 == handleData->frameRenderMode.bufferSize) {
        HDF_LOGE("Error: %{public}s, empty buf", __func__);
        return HDF_FAILURE;
    }

    PreProcessPcmData(aCtx, handleData->frameRenderMode.buffer, handleData->frameRenderMode.bufferSize);
    
    if (pcm_write(aCtx->pcm_handle, handleData->frameRenderMode.buffer, 
                    handleData->frameRenderMode.bufferSize) < 0) {
        HDF_LOGE("Error: pcm_write() failed: %{public}s\n", pcm_get_error(aCtx->pcm_handle));
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}


static int32_t DoOutputRenderStartPrepare(struct DevHandle *handle,
    const int cmdId, const struct AudioHwRenderParam *handleData)
{
    struct AlsaCtx *aCtx = ((struct AlsaDevObject*)handle->object)->pAlsaCtx;

    HDF_LOGV("INFO: enter %{public}s()", __func__);
    
    CHECK_IF_ALSA_OPENED(aCtx);

    // NOTE: depop here
    int bufsize = aCtx->config.period_count * aCtx->config.period_size;
    char *buf = calloc(1, bufsize);
    if (buf) {
        pcm_write(aCtx->pcm_handle, buf, bufsize);
        free(buf);
    }

    usleep(10000);
    
    return HDF_SUCCESS;
}


static int32_t DoOutputRenderStop(const struct DevHandle *handle,
    const int cmdId, const struct AudioHwRenderParam *handleData)
{
    struct AlsaCtx *aCtx = ((struct AlsaDevObject*)handle->object)->pAlsaCtx;

    HDF_LOGV("INFO: enter %{public}s()", __func__);
    
    CHECK_IF_ALSA_OPENED(aCtx);

    if (pcm_stop(aCtx->pcm_handle) < 0) {
        HDF_LOGE("Error: pcm_stop() failed. %{public}s", pcm_get_error(aCtx->pcm_handle));
        return HDF_FAILURE;
    }
    
    return HDF_SUCCESS;
}


static int32_t DoOutputRenderReqMmapBuffer(struct DevHandle *handle, int cmdId, struct AudioHwRenderParam *handleData)
{
    return HDF_SUCCESS;
}


static int32_t DoOutputRenderGetMmapPosition(struct DevHandle *handle, int cmdId, struct AudioHwRenderParam *handleData)
{
    return HDF_SUCCESS;
}


static int32_t HandleOutputRenderCmd(struct DevHandle *handle, const int cmdId, struct AudioHwRenderParam *handleData)
{
    int32_t ret;
    switch (cmdId) {
        case AUDIO_DRV_PCM_IOCTL_HW_PARAMS:
            ret = DoOutputRenderHwParams(handle, cmdId, handleData);
            break;
        case AUDIO_DRV_PCM_IOCTL_WRITE:
            ret = DoOutputRenderWrite(handle, cmdId, handleData);
            break;
        case AUDIO_DRV_PCM_IOCTRL_STOP:
            ret = DoOutputRenderStop(handle, cmdId, handleData);
            break;
        case AUDIO_DRV_PCM_IOCTRL_START:
        case AUDIO_DRV_PCM_IOCTL_PREPARE:
            ret = DoOutputRenderStartPrepare(handle, cmdId, handleData);
            break;
        case AUDIODRV_CTL_IOCTL_PAUSE_WRITE:
            ret = DoCtlRenderSetPauseStu(handle, cmdId, handleData);
            break;
        case AUDIO_DRV_PCM_IOCTL_MMAP_BUFFER:
            ret = DoOutputRenderReqMmapBuffer(handle, cmdId, handleData);
            break;
        case AUDIO_DRV_PCM_IOCTL_MMAP_POSITION:
            ret = DoOutputRenderGetMmapPosition(handle, cmdId, handleData);
            break;
        default:
            HDF_LOGE("Error: Output Mode not support!");
            ret = HDF_FAILURE;
            break;
    }
    return ret;
}



/************************************************************************************
    Exported Interface    
*************************************************************************************/

/* CreatRender for Bind handle */
struct DevHandle *AudioBindServiceRender(const char *name)
{
    struct DevHandle *handle = NULL;
    if (!name) {
        HDF_LOGE("Error: service name NULL!");
        return NULL;
    }
    
    HDF_LOGD("AudioBindServiceRender() name=%{public}s", name);

    if (strcmp(name, "render") && strcmp(name, "control")) {
        HDF_LOGE("Error: invalid service name.");
        return NULL;
    }
    
    handle = (struct DevHandle *)calloc(1, sizeof(struct DevHandle));
    if (!handle) {
        HDF_LOGE("Error: Failed to OsalMemCalloc handle");
        return NULL;
    }

    struct AlsaDevObject *devObject = calloc(1, sizeof(struct AlsaDevObject));
    if (!devObject) {
        HDF_LOGE("Error: alloc AlsaDevObject failed.");
        free(handle);
        return NULL;
    }

    snprintf(devObject->serviceName, sizeof(devObject->serviceName), "%s", name);
    devObject->pAlsaCtx = AlsaCtxInstanceGet();
    if (!strcmp(devObject->serviceName, "render")) {
        devObject->pAlsaCtx->renderRefCount++;
    }
    
    handle->object = devObject;
    
    HDF_LOGI("BIND SERVICE SUCCESS!");
    
    return handle;
}


void AudioCloseServiceRender(const struct DevHandle *handle)
{
    if (!handle || !handle->object) {
        HDF_LOGE("Error: Render handle or handle->object is NULL");
        return;
    }

    struct AlsaDevObject *devObject = (struct AlsaDevObject*)handle->object;
    if (!strcmp(devObject->serviceName, "render")) {
        if (--devObject->pAlsaCtx->renderRefCount <= 0) {
            // Do somthing here.
            devObject->pAlsaCtx->renderRefCount = 0;
        }
    }

    HDF_LOGD("AudioCloseServiceRender(), name=%{public}s", devObject->serviceName);
    
    free((void*)handle->object);
    free((void*)handle);

    HDF_LOGD("CLOSE SERVICE SUCCESS!");
    
    return;
}


int32_t AudioInterfaceLibModeRender(struct DevHandle *handle, struct AudioHwRenderParam *handleData, int cmdId)
{
    if (!handle || !handle->object || !handleData) {
        HDF_LOGE("Error: paras is NULL!");
        return HDF_FAILURE;
    }

    if (AUDIO_DRV_PCM_IOCTL_WRITE != cmdId) {
        HDF_LOGE("AudioInterfaceLibModeRender(cmdid=%{public}d)", cmdId);
    }
    
    switch (cmdId) {
        case AUDIO_DRV_PCM_IOCTL_HW_PARAMS:
        case AUDIO_DRV_PCM_IOCTL_WRITE:
        case AUDIO_DRV_PCM_IOCTRL_STOP:
        case AUDIO_DRV_PCM_IOCTRL_START:
        case AUDIO_DRV_PCM_IOCTL_PREPARE:
        case AUDIODRV_CTL_IOCTL_PAUSE_WRITE:
        case AUDIO_DRV_PCM_IOCTL_MMAP_BUFFER:
        case AUDIO_DRV_PCM_IOCTL_MMAP_POSITION:
            return HandleOutputRenderCmd(handle, cmdId, handleData);
        case AUDIODRV_CTL_IOCTL_ELEM_WRITE:
        case AUDIODRV_CTL_IOCTL_ELEM_READ:
        case AUDIODRV_CTL_IOCTL_MUTE_WRITE:
        case AUDIODRV_CTL_IOCTL_MUTE_READ:
        case AUDIODRV_CTL_IOCTL_GAIN_WRITE:
        case AUDIODRV_CTL_IOCTL_GAIN_READ:
        case AUDIODRV_CTL_IOCTL_CHANNEL_MODE_WRITE:
        case AUDIODRV_CTL_IOCTL_CHANNEL_MODE_READ:
        case AUDIODRV_CTL_IOCTL_SCENESELECT_WRITE:
        case AUDIODRV_CTL_IOCTL_GAINTHRESHOLD_READ:
        case AUDIODRV_CTL_IOCTL_ACODEC_CHANGE_IN:
        case AUDIODRV_CTL_IOCTL_ACODEC_CHANGE_OUT:
        case AUDIODRV_CTL_IOCTL_VOL_THRESHOLD_READ:
            return HandleCtlRenderCmd(handle, cmdId, handleData);
        default:
            HDF_LOGE("Error: Mode Error!");
            break;
    }
    return HDF_ERR_NOT_SUPPORT;
}

